/*
 * Personalizado.java
 *
 * Copyright 2011 Monica S. Cavalcante.
 * All rights reserved.
 *
 * This file is part of Mandatory Arrest
 *
 * MandatoryArrest is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * MandatoryArrest is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with MandatoryArrest. If not, see <http://www.gnu.org/licenses/>.
 *
 * Contributors:
 * Isabella Pessoa isabella.agp at gmail.com
 * Michell Jacks michelljacks at gmail.com
 * Monica Santana monicaasc.cc at gmail.com
 */

package br.edu.ufcg.dsc.lp2.projeto.model;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import br.edu.ufcg.dsc.lp2.projeto.model.exceptions.NumeroInvalidoException;

/**
 * Algoritmo personalizado de recomendacao. Recomenda a um usuario n locais que
 * ele nao conhece e que outros usuarios com gostos similares, dado a partir do
 * produto escalar, possuem opinioes maiores que 0.
 * 
 * @author Monica Santana monicaasc.cc at gmail.com
 * 
 */
public class Personalizado {

	/**
	 * Faz as recomendacoes ao usuario.
	 * 
	 * @param usuario
	 *            O Usuario a verificar.
	 * @param usuarios
	 *            Lista com todos os Usuarios cadastrados.
	 */
	public static List<Estabelecimento> retorna(String numero,
			Usuario usuario1, List<Usuario> usuarios,
			List<Estabelecimento> estabelecimentos) {
		if (numero == null || numero.equals("")) {
			throw new NumeroInvalidoException(
					"Numero nao pode ser vazio ou nulo");
		}
		int num = Integer.parseInt(numero);

		if (num <= 0) {
			throw new NumeroInvalidoException("Numero deve ser maior que 0");
		}

		List<Usuario> produtoEscalar = produtoEscalar(usuario1, usuarios);
		List<Estabelecimento> recomendados = new ArrayList<Estabelecimento>();
		List<Integer> opiniaoUser1 = usuario1.listaDeOpinioes();
		List<Integer> opiniaoUser2;
		Usuario usuario2;
		int indicados = 0;

		// Pega o resultado o produto escalar. Separa o nome do
		// Estabelecimento pra comparar as opinioes.
		Iterator<Usuario> it = produtoEscalar.iterator();
		while (it.hasNext()) {
			usuario2 = (Usuario) it.next();
			opiniaoUser2 = usuario2.listaDeOpinioes();

			for (int i = 0; i < opiniaoUser1.size(); i++) {
				System.out.println("User1: " + opiniaoUser1.get(i)
						+ " | User2: " + opiniaoUser2.get(i)
						+ " | Usuario indicando: " + usuario2.getNome()
						+ " | Local: " + estabelecimentos.get(i).getNome());
				if (num == indicados) {
					return recomendados;
				}
				if (opiniaoUser1.get(i) == 0 && opiniaoUser2.get(i) > 0) {
					System.out.println("---------\n"
							+ estabelecimentos.get(i).getNome()
							+ "-----\n User1: " + opiniaoUser1.get(i) + " - "
							+ opiniaoUser2.get(i));

					System.out.println("Indicacao: " + indicados);
					System.out.println("Local indicado: "
							+ estabelecimentos.get(i).getNome());
					System.out.println("Usuario que indicou: "
							+ usuario2.getNome());
					recomendados.add(estabelecimentos.get(i));
					indicados++;
				}
			}
		}
		return recomendados;
	}

	/**
	 * Calcula o produto escalar e adiciona num mapa. Onde a chave é o
	 * resultado e o valor um usuario.
	 * 
	 * @param usuario1
	 *            Usuario a comparar com os demais da lista.
	 * @param lista
	 *            Lista com todos os usuarios no lidos do arquivo.
	 * @return List com o resultado do produto escalar entre todos os usuarios.
	 */
	private static List<Usuario> produtoEscalar(Usuario usuario1,
			List<Usuario> usuarios) {
		List<Usuario> semelhancas = new ArrayList<Usuario>();

		Usuario usuario2;
		Iterator<Usuario> it = usuarios.iterator();
		while (it.hasNext()) {
			usuario2 = it.next();
			int result = 0;
			result = totalProdutoEscalar(usuario1, usuario2);
			usuario2.setSemelhanca(result);
			semelhancas.add(usuario2);
		}
		ordenaSemelhanca(semelhancas);
		return semelhancas;
	}

	/**
	 * Calcula o produto escalar entre dois usuarios.
	 * 
	 * @param usuario1
	 *            Um usuario.
	 * @param usuario2
	 *            Um usuario.
	 * @return Inteiro com o resultado do produto escalar.
	 */
	private static int totalProdutoEscalar(Usuario usuario1, Usuario usuario2) {
		int soma = 0;
		List<Integer> opiniaoUser1 = usuario1.listaDeOpinioes();
		List<Integer> opiniaoUser2 = usuario2.listaDeOpinioes();

		for (int i = 0; i < opiniaoUser1.size(); i++) {
			soma += opiniaoUser1.get(i) * opiniaoUser2.get(i);
		}
		return soma;
	}

	/**
	 * Ordena uma lista de usuarios a partir da semelhanca dos usuarios de forma
	 * decrescente.
	 * 
	 * @param usuarios
	 *            Lista de usuarios.
	 */
	private static void ordenaSemelhanca(List<Usuario> usuarios) {
		Collections.sort(usuarios, new Comparator<Object>() {
			public int compare(Object obj1, Object obj2) {
				Usuario p1 = (Usuario) obj1;
				Usuario p2 = (Usuario) obj2;
				return p1.getSemelhanca() < p2.getSemelhanca() ? +1 : 0;
			}
		});
	}

	public static double porcentagemAcertoRecomendacao(Usuario user,
			List<Usuario> usuarios, List<Estabelecimento> estabelecimentos) {

		List<Estabelecimento> estabEsperados = new ArrayList<Estabelecimento>();
		int contaPontoPositivo = 0;
		int contaPontoNegativo = 0;
		double porcentagem;

		Map<Estabelecimento, Integer> notas = user.getOpiniao();
		Iterator it = notas.entrySet().iterator();
		while (it.hasNext()) {
			Map.Entry pairs = (Map.Entry) it.next();
			Estabelecimento estabelecimento = (Estabelecimento) pairs.getKey();
			if (user.getNota(estabelecimento) > 0) {
				user.adicionaOpiniao(estabelecimento, 0);
				estabEsperados.add(estabelecimento);
			}
		}
		
//
//		Iterator it = notas.entrySet().iterator();
//		while (it.hasNext()) {
//			Estabelecimento estabelecimento = (Estabelecimento) it.next();
//			if (user.getNota(estabelecimento) > 0) {
//				user.adicionaOpiniao(estabelecimento, 0);
//				estabEsperados.add(estabelecimento);
//			}
//		}

		int quantRecomendacoes = estabelecimentos.size();
		List<Estabelecimento> resultadoRecomendacao = Personalizado.retorna(
				quantRecomendacoes+"", user, usuarios, estabelecimentos);

		for (Estabelecimento estabelecimento : estabEsperados) {
			if (resultadoRecomendacao.contains(estabelecimento)) {
				contaPontoPositivo++;
			} else {
				contaPontoNegativo++;
			}
		}

		return (contaPontoPositivo * 100.0)
				/ (contaPontoNegativo + contaPontoPositivo);
	}
}